package cn.melonlib.cores.commons.comp.service.impl;

import cn.melonlib.cores.commons.comp.enums.OrderByType;
import cn.melonlib.cores.commons.comp.utils.EntityWrapperContainer;
import cn.melonlib.cores.commons.model.entity.IEntity;
import cn.melonlib.cores.identification.comp.messenger.IdentificationMessenger;
import cn.melonlib.cores.jpa.comp.Dao;
import cn.melonlib.cores.jpa.model.bean.impl.DefaultQuery;
import cn.melonlib.cores.jpa.model.bean.impl.OffsetQueryImpl;
import cn.melonlib.cores.jpa.model.bean.impl.PaginationList;
import cn.melonlib.cores.model.anno.*;
import cn.melonlib.cores.model.enums.CachePolicy;
import cn.melonlib.cores.model.enums.QueryMode;
import cn.melonlib.cores.model.input.OrderByInput;
import cn.melonlib.objects.maps.JsonMap;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Getter;
import org.apache.commons.lang3.ObjectUtils;
import org.hibernate.service.spi.ServiceException;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 业务服务基础对象
 *
 * @param <ENTITY> 对应的存储对象
 */
@Getter
public class WorkerBasicService<ENTITY extends IEntity<? extends Serializable>> extends ApplicationObjectSupport {


    protected Class<ENTITY> entityClass;

    protected Class<? extends IEntity<? extends Serializable>> modelClass;

    private Class<? extends IEntity<? extends Serializable>> voClass;

    private Class<? extends IEntity<? extends Serializable>> inputClass;

    private Class<? extends IEntity<? extends Serializable>> filterClass;

    @Resource
    private ObjectMapper jsonMapper;

    @Resource
    @Lazy
    private IdentificationMessenger identificationMessenger;


    protected JsonMap<OrderByType> orderBy;

    @PostConstruct
    public void init() {
        Class cls = this.getClass();
        if (AnnotationUtils.findAnnotation(this.getClass(), WorkerService.class) != null) {
            WorkerModel model = AnnotationUtils.findAnnotation(this.getClass(), WorkerService.class).model();
            this.entityClass = (Class<ENTITY>) ResolvableType.forClass(this.getClass()).getSuperType().getGeneric(0).resolve();
            this.modelClass = (Class<? extends IEntity<? extends Serializable>>) AnnotationUtils.findAnnotation(this.getClass(), WorkerService.class).model().modelType();
            this.voClass = model.voType().equals(IEntity.class) ? modelClass : (Class<? extends IEntity<? extends Serializable>>) model.voType();
            this.inputClass = model.inputType().equals(IEntity.class) ? modelClass : (Class<? extends IEntity<? extends Serializable>>) model.inputType();
            this.filterClass = model.filterType().equals(IEntity.class) ? modelClass : (Class<? extends IEntity<? extends Serializable>>) model.filterType();
        }
        orderBy = defaultOrderBy();
    }

    protected JsonMap<OrderByType> defaultOrderBy() {
        return JsonMap.<OrderByType>createMap().append("id", OrderByType.ASC);
    }

    @Resource
    protected Dao dao;

    @Resource
    protected EntityWrapperContainer entityWrapperContainer;


    @WorkerQuery
    @WorkerComment("分页查询")
    public <DATA extends List<? extends IEntity<? extends Serializable>>> DATA list(
            @WorkerParameter(value = "filter", required = true, comment = "过滤条件") JsonMap<Object> filter,
            @WorkerParameter(value = "orderBy", comment = "排序方式") OrderByInput orderBy) throws Exception {
        JsonMap<JsonMap<Object>> params = JsonMap.createMap();
        if (ObjectUtils.isNotEmpty(filter)) {
            filter.entrySet().forEach(e -> {
                params.put(e.getKey(), JsonMap.createMap((Map<String, Object>) e.getValue()));
            });
        }
//        this.beforeSearch(first,offset,params);
        JsonMap<OrderByType> odrBy = JsonMap.createMap();
        if (orderBy != null) {
            odrBy.append(orderBy.getField(), OrderByType.valueOf(orderBy.getType()));
        } else {
            odrBy.putAll(this.orderBy);
        }
        return this.dao.<DATA, DefaultQuery>search(new DefaultQuery(this.entityClass, params, odrBy))
                .getResult();
    }

    @WorkerQuery(mode = QueryMode.MORE, cachePolicy = CachePolicy.CACHE_FIRST)
    @WorkerComment("游标查询")
    public <DATA extends PaginationList<ENTITY>> DATA search(@WorkerParameter(value = "first", required = true, comment = "获取数据量") Integer first,
                                                             @WorkerParameter(value = "offset", required = true, comment = "获取数据起始位置") Long offset,
                                                             @WorkerParameter(value = "filter", required = true, comment = "过滤条件") JsonMap<JsonMap<Object>> filter,
                                                             @WorkerParameter(value = "orderBy", comment = "排序方式") OrderByInput orderBy) throws Exception {
        JsonMap<JsonMap<Object>> params = JsonMap.createMap();
        if (ObjectUtils.isNotEmpty(filter)) {
            filter.entrySet().forEach(e -> {
                params.put(e.getKey(), JsonMap.createMap((Map<String, Object>) e.getValue()));
            });
        }
//        this.beforeSearch(first,offset,params);
        JsonMap<OrderByType> odrBy = JsonMap.createMap();
        if (orderBy != null) {
            odrBy.append(orderBy.getField(), OrderByType.valueOf(orderBy.getType()));
        } else {
            odrBy.putAll(this.orderBy);
        }
        return this.dao.<DATA, OffsetQueryImpl>search(new OffsetQueryImpl(this.entityClass, params, odrBy, offset, first.longValue()))
                .getResult();
    }

    protected <DATA extends PaginationList<ENTITY>, POST extends PaginationList<? extends IEntity<? extends Serializable>>> POST postSearch(DATA data) {
        return toGQL(data);
    }

    public <DATA extends PaginationList<? extends IEntity<? extends Serializable>>> DATA toGQL(PaginationList<ENTITY> datas) {
        if (this.entityClass != this.voClass) {
            List<? extends IEntity<? extends Serializable>> entities = datas.stream().map(data -> {
                try {
                    Constructor<? extends IEntity<? extends Serializable>> cons = this.voClass.getConstructor(this.entityClass);
                    return cons.newInstance(data);
                } catch (Exception e) {
                    throw new ServiceException(e.getMessage(), e);
                }

            }).collect(Collectors.toList());
            return (DATA) PaginationList.createOffset(entities, datas.getOffset(), datas.getLimit(), datas.getTotal());
        }
        return (DATA) datas;
    }


    @WorkerQuery(mode = QueryMode.ONCE, cachePolicy = CachePolicy.CACHE_FIRST)
    @WorkerComment("获取库中所有数据，数据量大时请谨慎使用")
    public PaginationList<ENTITY> loadAll() {
        List<ENTITY> all = this.dao.<List<ENTITY>, String>search("from " + this.entityClass.getSimpleName()).getResult();
        return toGQL(PaginationList.createOffset(all, 0, all.size(), all.size()));
    }

    @WorkerQuery
    @WorkerComment("统计库中所有对象的数据量")
    public Long totalCount() {
        List<Number> all = this.dao.<List<Number>, String>search("select count(*) from " + this.entityClass.getSimpleName()).getResult();
        return all.get(0).longValue();
    }

    @WorkerQuery(mode = QueryMode.ONCE, cachePolicy = CachePolicy.CACHE_FIRST)
    @WorkerComment("获取一个对象")
    public ENTITY getById(@WorkerParameter(value = "id", required = true, comment = "对象的ID值") String id) {
        return this.dao.getEntity(this.entityClass, id);
    }

    @Transactional(value = Transactional.TxType.REQUIRED, rollbackOn = Exception.class)
    @WorkerStore
    @WorkerComment("保存对象")
    public ENTITY save(@WorkerParameter(value = "entity", required = true, comment = "保存对象，无id值新增，有id值修改，如果修改未找到对象则抛出异常修改失败") ENTITY entity) {
        entity = doSave(entity);
        return entity;
    }

    protected ENTITY doSave(ENTITY entity) {
        return this.dao.save(entity).getResult();
    }

    public int remove(ENTITY entity) {
        return this.dao.remove(entity).getResult();
    }

    @Transactional(value = Transactional.TxType.REQUIRED, rollbackOn = Exception.class)
    @WorkerStore
    @WorkerComment("删除一个对象")
    public int removeById(@WorkerParameter(value = "id", required = true, comment = "对象ID值") String id) {
        ENTITY entity = this.dao.getEntity(this.entityClass, id);
        return this.dao.removeById(this.entityClass, id).getResult();
    }

    @Transactional(value = Transactional.TxType.REQUIRED, rollbackOn = Exception.class)
    @WorkerStore
    @WorkerComment("删除一批对象")
    public int removeByIdList(@WorkerParameter(value = "ids", required = true, comment = "对象ID值列表") List<String> ids) throws Exception {
        return this.dao.removeMultiByKeys(this.entityClass, ids).getResult();
    }


    public static interface ROOT_METHOD {
        public static final String LIST = "list";
        public static final String SEARCH = "search";
        public static final String LOAD_ALL = "loadAll";
        public static final String TOTAL_COUNT = "totalCount";
        public static final String GET_BY_ID = "getById";
        public static final String SAVE = "save";
        public static final String REMOVE_BY_ID = "removeById";
        public static final String REMOVE_BY_ID_LIST = "removeByIdList";
    }
}
